import FileSaver from "file-saver";
import XLSX from "xlsx";
import dayjs from "dayjs";
import { message as Message } from "element-ui"
import { isString } from "cl-admin/utils";
import ExcelJS from 'exceljs'
import { export_json_to_excel } from "./export2Excel";
const isDom = (el) => (el instanceof HTMLElement) && (el.nodeType == Node.ELEMENT_NODE)

/** *
 * 导出EXCEl
 * @parmas domName DOM元素的选择器 例如：#print 或者 .print
 */
export function exportExcel(el) {
	// 判断要导出的节点中是否有fixed的表格，如果有，转换excel时先将该dom移除，然后append回去，
	let table = null;
	if (isString(el)) {
		table = document.querySelector(el + " .el-table");
	} else if (isDom(el)) {
		table = el.querySelector(".el-table");
	}
	if (!table) {
		throw new Error('table is not found')
	}
	let fix = table.querySelector(".el-table__fixed");
	let fixr = table.querySelector(".el-table__fixed-right");
	let wb;
	// let xlsxParam = { raw: true } // 把excel格式设置成字符串格式
	if (fix || fixr) {
		fix && table.removeChild(fix);
		fixr && table.removeChild(fixr);
		wb = XLSX.utils.table_to_book(table);
		fix && table.appendChild(fix);
		fixr && table.appendChild(fixr);
	} else {
		wb = XLSX.utils.table_to_book(table);
	}
	/* 获取二进制字符串作为输出 */
	let wbout = XLSX.write(wb, {
		bookType: "xlsx",
		bookSST: true,
		type: "array"
	});
	try {
		FileSaver.saveAs(
			//Blob 对象表示一个不可变、原始数据的类文件对象。
			//Blob 表示的不一定是JavaScript原生格式的数据。
			//File 接口基于Blob，继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
			//返回一个新创建的 Blob 对象，其内容由参数中给定的数组串联组成。
			new Blob([wbout], { type: "application/octet-stream" }),
			//设置导出文件名称
			dayjs().format("YYYYMMDDhhmmss") + ".xlsx"
		);
	} catch (e) {
		if (typeof console !== "undefined") console.log(e, wbout);
	}
	return wbout;
}


/** *
 * 导出EXCEl
 * @parmas domName DOM元素的选择器 例如：#print 或者 .print
 */
// export function exportExcelCoustom(name, data = [], header = [], summaries = []) {
// 	// let fix = ele.querySelector(".el-table__fixed-header-wrapper").getElementsByTagName('thead')[0].childNodes;

// 	var data1 = [
// 		["编号", "名称1", "", ""],
// 		["编号", "名称", "价格", "日期"],
// 		[1, "《Java架构师》", "￥78.5", "2018-10-11"],
// 		[2, "《Python入门到精通》", "￥65.3", "2019-03-22"],
// 		[3, "《JavaScript高级教程》", "￥89.4", "2017-04-13"]
// 	];

// 	let headerArr = header.map(e => {
// 		return e.label
// 	})

// 	// let newArr = []
// 	let newArr = [headerArr]

// 	const getName = (obj) => {
// 		let newArr = []
// 		header.forEach(item => {
// 			Object.keys(obj).forEach(key => {
// 				if (item.prop == key) {
// 					newArr.push(obj[key])
// 				}
// 			})
// 		})

// 		console.info("newArr", newArr)
// 		return newArr
// 	}

// 	// const getName = (obj) => {
// 	// 	let newObj = {}
// 	// 	header.forEach(item => {
// 	// 		Object.keys(obj).forEach(key => {
// 	// 			if (item.prop == key) {
// 	// 				newObj[item["label"]] = obj[key]
// 	// 			}
// 	// 		})
// 	// 	})

// 	// 	console.info("newObj", newObj)
// 	// 	return newObj
// 	// }

// 	data.forEach(item => {
// 		newArr.push(getName(item))
// 	})

// 	newArr.push(summaries)
// 	console.info("newArr111", newArr)
// 	// return false

// 	let wb = XLSX.utils.book_new()
// 	// merges
// 	// var ws = XLSX.utils.json_to_sheet(newArr, { header: headerArr });
// 	var ws = XLSX.utils.aoa_to_sheet(data1);

// 	XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
// 	XLSX.writeFile(wb, `${name}.xlsx`);

// }

// *******************************************************************************
/** *
 * 导出EXCEl
 * @parmas sheetName 文件名字 例如：门店明细
 * @parmas data 表格数据
 * @parmas header 表格头部 {
 * 		label: '标题',
 * 		prop: 'title'
 * 		summable: 1, 如果该列需要合计,并且是数字类型
 * 		children: []  是否有子集列
 * }
 * @parmas summaries 表格合计数据，需要合计的话要传入此数组
 */
export function exportExcelCoustom(sheetName, data = [], header = [], summaries = []) {
	if (data.length == 0) {
		Message.warning('暂无数据！')
		return false
	}
	// excel表头
	let excelHeader = buildHeader(header);
	// 头部行数，用来固定表头
	let headerRows = excelHeader.length;
	// 提取数据
	// let dataList = this.extractData(this.selectionData, this.revealList);
	let dataList = extractData(data, header)
	dataList.push(summaries)
	console.log('dataList...', dataList)

	excelHeader.push(...dataList, []);
	// 计算合并
	let merges = doMerges(excelHeader)
	// 生成sheet
	let ws = aoa_to_sheet(excelHeader, headerRows);
	// 单元格合并
	ws['!merges'] = merges;
	// 头部冻结
	ws["!freeze"] = {
		xSplit: "1",
		ySplit: "" + headerRows,
		topLeftCell: "B" + (headerRows + 1),
		activePane: "bottomRight",
		state: "frozen"
	};
	// 列宽
	ws['!cols'] = [{ wpx: 165 }];
	let workbook = {
		SheetNames: [sheetName],
		Sheets: {}
	};
	workbook.Sheets[sheetName] = ws;
	// excel样式
	let wopts = {
		bookType: 'xlsx',
		bookSST: false,
		type: 'binary',
		cellStyles: true
	};
	let wbout = XLSX.write(workbook, wopts);
	let blob = new Blob([s2ab(wbout)], { type: "application/octet-stream" });
	openDownloadXLSXDialog(blob, sheetName + '.xlsx')
}
/**
* 根据选中的数据和展示的列，生成结果
* @param selectionData
* @param revealList
*/
function extractData(selectionData, revealList) {
	// 列
	let headerList = flat(revealList);
	// 导出的结果集
	let excelRows = [];
	// 如果有child集合的话会用到
	let dataKeys = new Set(Object.keys(selectionData[0]));
	selectionData.some(e => {
		if (e.children && e.children.length > 0) {
			let childKeys = Object.keys(e.children[0]);
			for (let i = 0; i < childKeys.length; i++) {
				dataKeys.delete(childKeys[i]);
			}
			return true;
		}
	})
	flatData(selectionData, (list) => {
		excelRows.push(...buildExcelRow(dataKeys, headerList, list));
	})
	return excelRows;
}
/**
 *
 *
 * */
function buildExcelRow(mainKeys, headers, rawDataList) {
	// 合计行
	let sumCols = [];
	// 数据行
	let rows = [];
	for (let i = 0; i < rawDataList.length; i++) {
		let cols = []
		let rawData = rawDataList[i];
		// 提取数据
		for (let j = 0; j < headers.length; j++) {
			let header = headers[j];
			// 父元素键需要行合并
			if (rawData['rowSpan'] === 0 && mainKeys.has(header.prop)) {
				cols.push('!$ROW_SPAN_PLACEHOLDER')
			} else {
				let value;
				if (typeof header.exeFun === 'function') {
					value = header.exeFun(rawData);
				} else {
					value = rawData[header.prop];
				}
				cols.push(value)
				// 如果该列需要合计,并且是数字类型
				if (header['summable'] && typeof value === 'number') {
					sumCols[j] = (sumCols[j] ? sumCols[j] : 0) + value;
				}
			}
		}
		rows.push(cols);
	}
	// 如果有合计行
	if (sumCols.length > 0) {
		rows.push(...sumRowHandle(sumCols));
	}
	return rows;
}
function sumRowHandle(sumCols) {
	//TODO
	return [];
}
/**
* @param list
* @param eachDataCallBack
*/
function flatData(list, eachDataCallBack) {
	let resultList = [];
	for (let i = 0; i < list.length; i++) {
		let data = list[i];
		let rawDataList = [];
		// 每个子元素都和父元素合并成一条数据
		if (data.children && data.children.length > 0) {
			for (let j = 0; j < data.children.length; j++) {
				delete data.children[j].bsm
				let copy = Object.assign({}, data, data.children[j]);
				rawDataList.push(copy);
				copy['rowSpan'] = (j > 0 ? 0 : data.children.length);
			}
		} else {
			data['rowSpan'] = 1;
			rawDataList.push(data);
		}
		resultList.push(...rawDataList);
		if (typeof eachDataCallBack === 'function') {
			eachDataCallBack(rawDataList)
		}
	}
	return resultList;
}
/**
 * 构建excel表头
 * @param revealList 列表页面展示的表头
 * @returns {[]} excel表格展示的表头
 */
function buildHeader(revealList) {
	let excelHeader = [];
	// 构建生成excel表头需要的数据结构
	getHeader(revealList, excelHeader, 0, 0);
	// 多行表头长短不一，短的向长的看齐，不够的补上行合并占位符
	let max = Math.max(...(excelHeader.map(a => a.length)))
	excelHeader.filter(e => e.length < max).forEach(
		e => pushRowSpanPlaceHolder(e, max - e.length))
	return excelHeader;
}
/**
 * 生成头部
 * @param headers 展示的头部
 * @param excelHeader excel头部
 * @param deep 深度
 * @param perOffset 前置偏移量
 * @returns {number}  后置偏移量
 */
const getHeader = (headers, excelHeader, deep, perOffset) => {
	let offset = 0
	let cur = excelHeader[deep]
	if (!cur) {
		cur = excelHeader[deep] = []
	}
	// 填充行合并占位符
	pushRowSpanPlaceHolder(cur, perOffset - cur.length)
	for (let i = 0; i < headers.length; i++) {
		let head = headers[i]
		cur.push(head.label)
		if (head.hasOwnProperty('children') && Array.isArray(head.children) && head.children.length > 0) {
			let childOffset = getHeader(head.children, excelHeader, deep + 1,
				cur.length - 1)
			// 填充列合并占位符
			pushColSpanPlaceHolder(cur, childOffset - 1)
			offset += childOffset
		} else {
			offset++
		}
	}
	return offset;
}
/**
 * 合并头部单元格
 **/
function doMerges(arr) {
	// 要么横向合并 要么纵向合并
	let deep = arr.length;
	let merges = [];
	for (let y = 0; y < deep; y++) {
		// 先处理横向合并
		let row = arr[y];
		let colSpan = 0
		for (let x = 0; x < row.length; x++) {
			if (row[x] === '!$COL_SPAN_PLACEHOLDER') {
				row[x] = undefined;
				if (x + 1 === row.length) {
					merges.push({ s: { r: y, c: x - colSpan - 1 }, e: { r: y, c: x } })
				}
				colSpan++
			} else if (colSpan > 0 && x > colSpan) {
				merges.push({ s: { r: y, c: x - colSpan - 1 }, e: { r: y, c: x - 1 } })
				colSpan = 0
			} else {
				colSpan = 0
			}
		}
	}
	// 再处理纵向合并
	let colLength = arr[0].length
	for (let x = 0; x < colLength; x++) {
		let rowSpan = 0
		for (let y = 0; y < deep; y++) {
			if (arr[y][x] === '!$ROW_SPAN_PLACEHOLDER') {
				arr[y][x] = undefined;
				if (y + 1 === deep) {
					merges.push({ s: { r: y - rowSpan, c: x }, e: { r: y, c: x } })
				}
				rowSpan++;
			} else if (rowSpan > 0 && y > rowSpan) {
				merges.push({ s: { r: y - rowSpan - 1, c: x }, e: { r: y - 1, c: x } })
				rowSpan = 0;
			} else {
				rowSpan = 0;
			}
		}
	}
	return merges;
}
// 扁平头部
function flat(revealList) {
	let result = []
	revealList.forEach(e => {
		if (e.hasOwnProperty('children')) {
			result.push(...flat(e.children))
		} else if (e.hasOwnProperty('exeFun')) {
			result.push(e)
		} else if (e.hasOwnProperty('prop')) {
			result.push(e)
		}
	})
	return result;
}
function s2ab(s) {
	let buf = new ArrayBuffer(s.length);
	let view = new Uint8Array(buf);
	for (let i = 0; i !== s.length; ++i) {
		view[i] = s.charCodeAt(i) & 0xFF;
	}
	return buf;
}
/**
 * 从github复制过来的
 */
function aoa_to_sheet(data, headerRows) {
	const ws = {};
	const range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
	for (let R = 0; R !== data.length; ++R) {
		for (let C = 0; C !== data[R].length; ++C) {
			if (range.s.r > R) {
				range.s.r = R;
			}
			if (range.s.c > C) {
				range.s.c = C;
			}
			if (range.e.r < R) {
				range.e.r = R;
			}
			if (range.e.c < C) {
				range.e.c = C;
			}
			/// 这里生成cell的时候，使用上面定义的默认样式
			const cell = {
				v: data[R][C] ?? '',
				s: {
					font: { name: "宋体", sz: 11, color: { auto: 1 } },
					alignment: {
						/// 自动换行
						wrapText: 1,
						// 居中
						horizontal: "center",
						vertical: "center",
						indent: 0
					}
				}
			};
			// 头部列表加边框
			if (R < headerRows) {
				cell.s.border = {
					top: { style: 'thin', color: { rgb: "000000" } },
					left: { style: 'thin', color: { rgb: "000000" } },
					bottom: { style: 'thin', color: { rgb: "000000" } },
					right: { style: 'thin', color: { rgb: "000000" } }
				};
				cell.s.fill = {
					patternType: 'solid',
					fgColor: { theme: 3, "tint": 0.3999755851924192, rgb: 'DDD9C4' },
					bgColor: { theme: 7, "tint": 0.3999755851924192, rgb: '8064A2' }
				}
			}
			const cell_ref = XLSX.utils.encode_cell({ c: C, r: R });
			if (typeof cell.v === 'number') {
				cell.t = 'n';
			} else if (typeof cell.v === 'boolean') {
				cell.t = 'b';
			} else {
				cell.t = 's';
			}
			ws[cell_ref] = cell;
		}
	}
	if (range.s.c < 10000000) {
		ws['!ref'] = XLSX.utils.encode_range(range);
	}
	return ws;
}

/**
 * 填充行合并占位符
 * */
function pushRowSpanPlaceHolder(arr, count) {
	for (let i = 0; i < count; i++) {
		arr.push('!$ROW_SPAN_PLACEHOLDER')
	}
}
// 填充列合并占位符
function pushColSpanPlaceHolder(arr, count) {
	for (let i = 0; i < count; i++) {
		arr.push('!$COL_SPAN_PLACEHOLDER')
	}
}
function openDownloadXLSXDialog(url, saveName) {
	if (typeof url == 'object' && url instanceof Blob) {
		url = URL.createObjectURL(url); // 创建blob地址
	}
	var aLink = document.createElement('a');
	aLink.href = url;
	aLink.download = saveName || ''; // HTML5新增的属性，指定保存文件名，可以不要后缀，注意，file:///模式下不会生效
	var event;
	if (window.MouseEvent) {
		event = new MouseEvent('click');
	} else {
		event = document.createEvent('MouseEvents');
		event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false,
			false, false, false, 0, null);
	}
	aLink.dispatchEvent(event);
}


/**
 * @param {array} columns
 * @param {array} list
 * @param {string} filename
 * @returns {Workbook} workbook
 */
export async function exportJsonToExcel(columns = [], list = [], filename) {
	const { header } = eachColumn(columns)
	let data = eachData(columns, list)
	// console.log({ columns, header, data });
	// export_json_to_excel({ header, data, filename: filename || dayjs().format("YYYYMMDDhhmmss") })
	let wb = new ExcelJS.Workbook();
	let sheet = wb.addWorksheet();
	// 按ID获取表格
	let ws = wb.getWorksheet(sheet.id);
	ws.addRow(header)
	ws.addRows(data)
	setWsStyle(ws, columns)
	const buffer = await wb.xlsx.writeBuffer();
	let nowTime = dayjs().format('YYYYMMDDHHmmss');
	let fileName = `${filename || nowTime}.xlsx`;
	const blob = new Blob([buffer], { type: '' });
	saveAs(blob, fileName);
	return wb
}
/**
 * @param {Worksheet} ws
 * @param {array} columns
 */
function setWsStyle(ws, columns) {
	// ws.headerFooter.differentFirst = true;
	// ws.headerFooter.oddHeader = '&L &D &T';
	// ws.headerFooter.oddFooter = '第 &P 页，共 &N 页';
	ws.eachRow((R, i) => {
		R.alignment = { vertical: 'middle', horizontal: 'left', wrapText: false };
		R.eachCell((C, j) => {
			let style = columns[j - 1].style
			if (!style) return
			const { head, cell } = style
			C.font = {}
			if (i == 1) {
				if (!head) return
				if (head.color) {
					C.font.color = { argb: head.color }
				}
				if (head.bgColor) {
					C.fill.bgColor = { argb: head.bgColor }
				}
				return
			}
			if (!cell) return
			if (cell.color) {
				C.font.color = { argb: cell.color }
			}
			if (cell.bgColor) {
				C.fill.bgColor = { argb: cell.bgColor }
			}
		});
	});

}
/**
 * @param {array} columns
 */
function eachColumn(columns = []) {
	let header = []
	// let keys = []
	for (let i = 0; i < columns.length; i++) {
		let column = columns[i]
		if (column?.prop == 'opt') break;
		header.push(column.label)
		// if (column.type == 'index') {
		// 	keys.push('index')
		// } else {
		// 	keys.push(column.prop)
		// }
	}
	return {
		header
		// keys
	}
}
/**
 * @param {array} columns
 * @param {array} list
 */
function eachData(columns = [], list = []) {
	let data = []
	// let keys = []
	for (let i = 0; i < list.length; i++) {
		let item = list[i]
		let arr = []
		for (let j = 0; j < columns.length; j++) {
			let column = columns[j]
			if (column?.prop == 'opt') break;
			if (column.type == 'index') {
				arr.push(i + 1)
			} else if (column.formatter) {
				arr.push(column.formatter(item) ?? '')
			} else {
				arr.push(item[column.prop] ?? '')
			}
		}
		data.push(arr)
	}
	return data
}
